/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
//package pl;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;

/**
 *
 * @author sempere
 */
public final class AnalitzadorLexic {

    private class enllas {

        Node seguent;
        Character transicio;
        ArrayList<Character> parelles;

        //Si transició es NULL, quiere decir que hablamos de ids o números,
        //por tanto tendremos que consultar pares de intervalos del arrayList;
        public enllas(Node seguent, Character transicio, ArrayList<Character> parellesDAcotacio) {
            this.seguent = seguent;
            this.transicio = transicio;
            this.parelles = parellesDAcotacio;
        }
    }

    private class Node {

        public ArrayList<enllas> enllassos;
        public String nomElement;
        public String cadena;
        public boolean accepta;
        int calTornar;

        public Node(String nomLexic, String cadenaAMostrar, int numElementsQueCalTornar, boolean acceptacio) {
            nomElement = nomLexic;
            cadena = cadenaAMostrar;
            calTornar = numElementsQueCalTornar;
            accepta = acceptacio;
        }

        public void setEnllassos(ArrayList<enllas> nousEnllassos) {
            enllassos = nousEnllassos;
        }

        public Node obtinAmbChar(char transicio) {
            char A, B;
            for (enllas e : enllassos) {
                if (e.transicio != null && e.transicio == transicio) {
                    return (e.seguent);
                } else if (e.parelles != null) {
                    for (int i = 0; i + 1 < e.parelles.size(); i++) {
                        A = e.parelles.get(i);
                        B = e.parelles.get(i + 1);
                        if (((int) A) <= transicio && ((int) B) >= transicio) {
                            return e.seguent;
                        }
                    }
                } else if (e.transicio == null && e.parelles == null) {
                    return (e.seguent);
                }
            }
            return null;
        }
    }
    private Node arrel;
    private RandomAccessFile arxiu;
    private int fila, columna;

    public AnalitzadorLexic(String ruta) throws FileNotFoundException {
        Node pari, pard, addop1, addop2, mulop1, mulop2, mulop2b, pyc, asig, llavei, llaved,
                num1, numOk, numPunt, numDecimal, numDecimalOk, id, idOk, sumCom1, sumCom2, sumCom3;

        obreFitxer(ruta);
        buffer = new bufferLlegits();

        fila = 1;
        columna = 0;

        ArrayList<enllas> enllassos;
        ArrayList<Character> parelles;

        arrel = new Node("arrel", "arrel", 0, false);

        //Nivell 1, de l'arrel a tots

        pari = new Node("pari", "(", 0, true);
        pard = new Node("pard", ")", 0, true);
        addop1 = new Node("addop", "+", 0, true);
        addop2 = new Node("addop", "-", 0, true);
        mulop1 = new Node("mulop", "*", 0, true);
        mulop2 = new Node("CasiMulop", "/", -1, false);
        mulop2b = new Node("mulop", "/", 1, true);
        pyc = new Node("pyc", ";", 0, true);
        asig = new Node("asig", "=", 0, true);
        llavei = new Node("llavei", "{", 0, true);
        llaved = new Node("llaved", "}", 0, true);
        num1 = new Node("num1", "quasiEntero", -1, false);
        numOk = new Node("entero", "entero", 1, true);
        numPunt = new Node("numPunt", "puntDeReal", -1, false);
        numDecimal = new Node("numDecimal", "quasiReal", -1, false);
        numDecimalOk = new Node("real", "real", 1, true);
        id = new Node("id", "quasiId", -1, false);
        idOk = new Node("id", "id", 1, true);
        sumCom1 = new Node("sumCom1", "comentari*", -1, false);
        sumCom2 = new Node("sumCom2", "comentari*2", -1, false);
        sumCom3 = new Node("sumCom3", "comentari/", 0, true);

        enllassos = new ArrayList<enllas>();
        enllassos.add(new enllas(pari, '(', null));
        enllassos.add(new enllas(pard, ')', null));
        enllassos.add(new enllas(addop1, '+', null));
        enllassos.add(new enllas(addop2, '-', null));
        enllassos.add(new enllas(mulop1, '*', null));
        enllassos.add(new enllas(mulop2, '/', null));
        enllassos.add(new enllas(pyc, ';', null));
        enllassos.add(new enllas(asig, '=', null));
        enllassos.add(new enllas(llavei, '{', null));
        enllassos.add(new enllas(llaved, '}', null));

        parelles = new ArrayList<Character>();
        parelles.add('0');
        parelles.add('9');
        enllassos.add(new enllas(num1, null, parelles));

        parelles = new ArrayList<Character>();
        parelles.add('A');
        parelles.add('Z');
        parelles.add('a');
        parelles.add('z');
        enllassos.add(new enllas(id, null, parelles));

        parelles = new ArrayList<Character>();
        parelles.add(' ');
        parelles.add(' ');
        parelles.add('\n');
        parelles.add('\n');
        parelles.add('\r');
        parelles.add('\r');
        parelles.add('\t');
        parelles.add('\t');
        enllassos.add(new enllas(arrel, null, parelles));
        arrel.enllassos = enllassos;


        //Comentaris

        enllassos = new ArrayList<enllas>();
        enllassos.add(new enllas(sumCom1, '*', null));
        enllassos.add(new enllas(mulop2b, null, null));
        mulop2.enllassos = enllassos;

        enllassos = new ArrayList<enllas>();
        enllassos.add(new enllas(sumCom2, '*', null));

        enllassos.add(new enllas(sumCom1, null, null));
        sumCom1.enllassos = enllassos;

        enllassos = new ArrayList<enllas>();
        enllassos.add(new enllas(sumCom3, '/', null));
        enllassos.add(new enllas(sumCom2, '*', null));
        enllassos.add(new enllas(sumCom1, null, null));
        sumCom2.enllassos = enllassos;





        //INT a INT, int a REAL i int a OK

        enllassos = new ArrayList<enllas>();
        parelles = new ArrayList<Character>();
        parelles.add('0');
        parelles.add('9');
        enllassos.add(new enllas(num1, null, parelles));
        enllassos.add(new enllas(numPunt, '.', null));
        enllassos.add(new enllas(numOk, null, null));
        num1.enllassos = enllassos;

        //DE PUNT A REAL, DE REAL A OK

        enllassos = new ArrayList<enllas>();
        parelles = new ArrayList<Character>();
        parelles.add('0');
        parelles.add('9');
        enllassos.add(new enllas(numDecimal, null, parelles));
        numPunt.enllassos = enllassos;

        enllassos = new ArrayList<enllas>();
        parelles.add('0');
        parelles.add('9');
        enllassos.add(new enllas(numDecimal, null, parelles));
        enllassos.add(new enllas(numDecimalOk, null, null));
        numDecimal.enllassos = enllassos;

        //DE ID a idOK

        enllassos = new ArrayList<enllas>();
        parelles = new ArrayList<Character>();
        parelles.add('A');
        parelles.add('Z');
        parelles.add('a');
        parelles.add('z');
        parelles.add('0');
        parelles.add('9');
        enllassos.add(new enllas(id, null, parelles));
        enllassos.add(new enllas(idOk, null, null));
        id.enllassos = enllassos;



        //...Escura el caldero...
    }
    // Ara mètodes de lectura i obtinció de tokens
    bufferLlegits buffer;

    private class trosDeToken {

        int c;
        int fila;
        int columna;
    }

    private class bufferLlegits {

        int numllegits;
        ArrayList<Integer> caracters;
        ArrayList<Integer> files;
        ArrayList<Integer> columnes;
        String cadenaActual;

        public bufferLlegits() {
            cadenaActual = "";
            numllegits = 0;
            caracters = new ArrayList<Integer>();
            files = new ArrayList<Integer>();
            columnes = new ArrayList<Integer>();
        }

        public void ficaDarrere(Integer caracter, int fila, int columna) {
            caracters.add(caracter);
            files.add(fila);
            columnes.add(columna);
            cadenaActual += (char) (int) caracter;
        }

        public trosDeToken obtinPrimerCaracter() {
            trosDeToken t = new trosDeToken();
            t.c = caracters.get(numllegits);
            t.fila = files.get(numllegits);
            t.columna = columnes.get(numllegits);
            numllegits++;
            return t;
        }

        public void retorna(int numTornes) {

            cadenaActual = cadenaActual.substring(numllegits - numTornes);
            while (numllegits > numTornes) {
                caracters.remove(0);
                files.remove(0);
                columnes.remove(0);
                numllegits--;
            }

            numllegits = 0;

        }

        public String obtinCadena(Node n) {
            String cadenaAux = cadenaActual.substring(0, cadenaActual.length() - n.calTornar);
            buffer.retorna(n.calTornar);
            while (cadenaAux.length() > 0 && esSeparador(cadenaAux.charAt(0))) {
                cadenaAux = cadenaAux.substring(1);
            }
            return (cadenaAux);
        }

        public boolean buit() {
            return (caracters.isEmpty());
        }
    }

    private boolean esSeparador(char c) {
        if (c == ' ' || c == '\b' || c == '\r' || c == '\n') {
            return true;
        } else {
            return false;
        }
    }

    private void obreFitxer(String ruta) throws FileNotFoundException {
        arxiu = new RandomAccessFile(ruta, "r");

    }

    private trosDeToken llig(boolean buidantBuffer) throws IOException {
        int i = -1;
        trosDeToken c;
        if (buffer.buit() || !buidantBuffer) {
            i = arxiu.read();
            columna++;
            buffer.ficaDarrere(i, fila, columna);
            if (i == (int) '\n') {
                fila++;
                columna = 0;
            }
        }

        c = buffer.obtinPrimerCaracter();
        return (c);

    }

    private void retorna(int numTornes) {
        buffer.retorna(numTornes);
    }

    boolean esperanza= true;
    public oldToken esprem() throws IOException, oldToken {

        Node pas = arrel, prePas = null;
        trosDeToken c, d, primero = null;
        oldToken tornada = null;
        String caracter = "";

        c = llig(true);
        d = c;
        while (c.c != -1) {
            if (primero == null && !esSeparador((char) c.c)) {
                primero = c;
            }
            prePas = pas;
            pas = pas.obtinAmbChar((char) c.c);
            if (pas == null) {
                break;
            } else if (pas.accepta) {
                tornada = new oldToken(pas.nomElement, buffer.obtinCadena(pas), primero.fila, primero.columna);

                if (tornada.token.equals("sumCom3")) {
                    return (esprem());
                } else {
                    return (tornada);
                }
            }
            d = c;
            c = llig(false);
        }


        //Tractament d'errors
        if (prePas == arrel && c.c != -1) {
            caracter += (char) c.c;
            throw new oldToken("ERROR1", caracter, c.fila, c.columna);
        } else if (pas == null && d.c != -1) {
            caracter += (char) d.c;
            throw new oldToken("ERROR1", caracter, d.fila, d.columna);
        } else if (pas.nomElement.equals("sumCom1")) {
            throw new oldToken("ERROR2", "", 0, 0);
        } else if (c.c==-1 && tornada == null && esperanza){
            esperanza = false;
            Node n= pas.obtinAmbChar(';');

            return (new oldToken(pas.nomElement,"final de fichero",c.fila,c.columna));
        }
        //System.out.println(pas.nomElement);

        return tornada;
    }
}
